
/**
 *
 * @author group_0217
 */
import java.util.HashMap;

public class Directory extends Data {
    // The folder in the file system.

    // The HashMap contains the contents of the Directory.
    private HashMap<String, Data> Contents;

    /**
     * Sets the name of the Directory object.
     * Create the Contents HashMap.
     * @param name the name of the Directory.
     */
    public Directory(String name) {
        super(name);
        // Contents of the Directory
        this.Contents = new HashMap<String, Data>();
        this.Contents.put(".", this);
    }

    /**
     * 
     * @return Contents which is a HashMap
     */
    public HashMap<String, Data> getDir() {
        return Contents;
    }

    /**
     * Takes a String key.
     * @param key the name of Data to be checked.
     * @return true if this Directory contains a File or Directory 
     * whose name is key. Ohterwise, return false.
     */
    public boolean ifContain(String key) {
        return Contents.containsKey(key);
    }

    /**
     * Takes a String name. Create a Directory with the name.
     * @param name the name of Directory to be created.
     * @throws FileExistError if a Directory or File with name already exists.
     * @throws JShellError if the name is JShell.
     */
    public void createDirectory(String name) throws FileExistError, JShellError {
        if (this.ifContain(name)) {
            // File already exists.
            throw new FileExistError(name + ": File exists");
        } else if (name.equals("JShell")) {
            // Can never create another Directory called JShell.
            throw new JShellError("Create JShell: Permission denied");
        } else {
            Directory newDirectory = new Directory(name);
            // Set parent of the new Directory.
            newDirectory.setParent(this);
            Contents.put(name, newDirectory);

        }
    }

    /**
     * Takes a String name. Create a File with the name.
     * @param name the name of File to be created.
     * @throws FileExistError if a Directory or File with name already exists.
     * @throws JShellError if the name is JShell.
     */
    public void createFile(String name) throws FileExistError, JShellError {
        if (this.ifContain(name)) {
            // File alredy exists.
            throw new FileExistError(name + ": File exists");
        } else if (name.equals("JShell")) {
            // Can never create a File called JShell.
            throw new JShellError("Create JShell: Permission denied");
        } else {
            File newFile = new File(name);
            // Set parent of the new File.
            newFile.setParent(this);
            Contents.put(name, newFile);
        }
    }

    /**
     * Print the names of all the Directories and Files in this.
     */
    @Override
    public void list() {
        // Get all the names of contents.
        Object[] contents = Contents.keySet().toArray();
        for (Object i : contents) {
            // "." means the Directory itself.
            // ".." means the parent Directory.
            if (!(i.equals(".") || i.equals(".."))) {
                System.out.println(i);
            }
        }
        System.out.println("");
    }

    /**
     * Delete the File or Directory with itemName in this.
     * @param itemName the name of Data to be deleted.
     */
    public void remove(String itemName) {
        if (this.ifContain(itemName)) {
            // Remove the item in the current folder.
            Contents.remove(itemName);
        } else {
            System.out.println(itemName + ": No such file or directory");
        }
    }

    @Override
    /**
     * Set the parent Directory.
     * @ param the name of the parent Directory.
     */
    public void setParent(Directory parent) {
        this.parent = parent;
        this.Contents.put("..", parent);
        this.Contents.put(".", this);
    }

    /**
     * Print the contents of File(name).
     * @param name the name of the File to be printed.
     * @throws NoDirectoryError if File not exists.
     * @throws NotFileError if its a Directory instead of a File.
     */
    public void openFile(String name) throws NoDirectoryError, NotFileError {
        if (!this.ifContain(name)) {
            // If no such a File.
            throw new NoDirectoryError(name + ": No such file or directory");
        } else if (this.Contents.get(name) instanceof Directory) {
            // If the given name is a Directory.
            throw new NotFileError(name + ": Is a directory");
        } else {
            File f = (File) this.Contents.get(name);
            f.display();
        }
    }

    /**
     * Append Contents to the File(name).
     * @param name the name of the File to be operated.
     * @param Contents the contents to be appended.
     */
    public void appendFile(String name, String Contents) {
        try {
            this.createFile(name);
            File f = (File) this.Contents.get(name);
            f.append(Contents);
        } catch (Exception e) {
            if (e instanceof FileExistError) {
                // If File already exists, append the String.
                File f = (File) this.Contents.get(name);
                f.append(Contents);
            } else {
                // If other Exception happans, like if it is a Directory under
                // the name
                System.out.println(e.getMessage());
            }
        }
    }

    /**
     * Overwrite the contents of File(name) with Contents.
     * @param name the name of the File to be operated.
     * @param Contents the contents to overwrite.
     */
    public void overwriteFile(String name, String Contents) {
        try {
            this.createFile(name);
            File f = (File) this.Contents.get(name);
            f.append(Contents);
        } catch (Exception e) {
            if (e instanceof FileExistError) {
                // If File already exists, overwrite the contents.
                File f = (File) this.Contents.get(name);
                f.overwrite(Contents);
            } else {
                // If other Exception happens, like it is a Directory under
                // the name.
                System.out.println(e.getMessage());
            }
        }
    }

    /**
     * A helper function of copy
     * @param d, currenty working on directory
     */
    private void helpCopy(Directory d) {
        //given Directory, and recursively copy all
        //of the contents in Directory and stored in Directory d. 
        Object[] contents = this.Contents.keySet().toArray();
        for (Object el : contents) {
            if (!el.equals(".") && !el.equals("..")) {
                if (this.Contents.get(el) instanceof Directory) {
                    try {
                        d.createDirectory(el.toString());
                        this.getDir().get(el).helpCopy(d.getDir().get(el));
                    } catch (Exception e) {
                        System.out.print("invalid copy");
                    }
                } else {
                    //copying file object to d
                    d.getDir().put((String) el, this.Contents.get(el).copy());
                    d.getDir().get((String) el).setParent(d.getDir());
                }
            }
        }
    }

    /**
     * Give a copy of object Directory
     * @return d, a copy Directory object of Directory
     */
    @Override
    public Directory copy() {
        //return a new copy of Directory
        Directory d = new Directory(this.returnName());
        this.helpCopy(d);
        return d;
    }

    @Override
    public String grep(String regex) {
        throw new UnsupportedOperationException("Not supported yet.");
    }
}
